using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;
using System.Text.RegularExpressions;
using System.Globalization;
using System.Linq;

using ERPStore.Extensions;

using CloudSoft.Extensions;

namespace ERPStore.Exporter.Services
{
	/// <summary>
	/// Attribut pour la description des colonnes pour la serialization CSV
	/// </summary>
	[AttributeUsage(AttributeTargets.Property)]
	public class ColumnAttribute : Attribute
	{
		public string Name;
		public int FieldIndex = 0;
		public string Format;

		public ColumnAttribute()
		{

		}
	}

	/// <summary>
	/// Classe pour la serialisation des classes d'import/export au format CSV
	/// </summary>
	/// <typeparam name="T"></typeparam>
	public static class ShopzillaSerializer<T>
    {
		static BindingFlags options = BindingFlags.Instance | BindingFlags.GetProperty | BindingFlags.Public;
		static RegexOptions regOptions = ((RegexOptions.IgnorePatternWhitespace
								 | RegexOptions.Singleline)
								 | RegexOptions.IgnoreCase);

		/// <summary>
		/// Deserializes the specified input.
		/// </summary>
		/// <param name="input">The input.</param>
		/// <returns></returns>
        public static T Deserialize(string input)
        {
            return (T)Deserialize(typeof(T), input);
        }

		private static object Deserialize(Type t, string input)
		{
			object result = Activator.CreateInstance(t);
			PropertyInfo[] list = t.GetProperties(options);

			string separator = "|";
			Regex reg = new Regex("((?:[^\",]|(?:\"(?:\\{2}|\\\"|[^\"])*?\"))*)", regOptions);
			MatchCollection coll = reg.Matches(input);
			List<string> items = new List<string>();
			int loop = 0;
			foreach (Match m in coll)
			{
				if (loop % 2 == 0)
				{
					items.Add(m.Groups[0].Value.Trim('"').Trim(',').Trim('"').Trim());
				}
				loop++;
			}
			int pos = 0;
			CultureInfo ci = new CultureInfo("en-US");
			foreach (PropertyInfo pi in list)
			{
				var attrs = pi.GetCustomAttributes(typeof(ColumnAttribute), true);
				if (attrs != null && attrs.Length > 0)
				{
					string item = string.Empty;
					try
					{
						item = items[pos];
						pos++;
					}
					catch
					{
						throw;
					}
					object o = null;
					if (pi.PropertyType == typeof(DateTime))
					{
						int year = int.Parse(item.Substring(0, 4));
						int month = int.Parse(item.Substring(4, 2));
						int day = int.Parse(item.Substring(6, 2));
						o = new DateTime(year, month, day);
					}
					else
					{
						try
						{
							o = System.Convert.ChangeType(item, pi.PropertyType, ci);
						}
						catch (Exception)
						{
							throw;
						}
					}

					try
					{
						if (o is string)
						{
							o = ((string)o).Replace("$CRLF$", "\r\n");
						}
						pi.SetValue(result, o, null);
					}
					catch (Exception)
					{
						throw;
					}
				}
			}

			return result;
		}


		/// <summary>
		/// Serializes the specified input.
		/// </summary>
		/// <param name="input">The input.</param>
		/// <returns></returns>
        public static string Serialize(T input, string separator = ";", string cultureInfo = "en-US")
        {
			var list = from pi in input.GetType().GetProperties(BindingFlags.Instance | BindingFlags.GetProperty | BindingFlags.Public)
					   let attrs = pi.GetCustomAttributes(typeof(ColumnAttribute), true)
					   where attrs != null && attrs.Length == 1
					   let att = attrs[0] as ColumnAttribute
					   select new { PropertyInfo = pi, CSVColumn = att };

            var ci = new System.Globalization.CultureInfo(cultureInfo);

            var result = new List<string>();

            foreach (var item in list.OrderBy(i => i.CSVColumn.FieldIndex))
            {
				object value = item.PropertyInfo.GetValue(input, null);
                string s = string.Empty;
				if (item.PropertyInfo.PropertyType == typeof(string))
                {
					if (value == null || (string)value == "")
					{
						s = string.Empty;
					}
					else
					{
						s = value.ToString().Replace(separator, " ");
					}
                }
				else if (item.PropertyInfo.PropertyType == typeof(DateTime))
	            {
					var f = item.CSVColumn.Format ?? "{0:yyyyMMdd}";
		            s = "\"" + string.Format(f, value) + "\"";
	            }
				else if (item.PropertyInfo.PropertyType == typeof(decimal))
                {
					var f = item.CSVColumn.Format ?? "{0:f2}";
                    s = string.Format(ci,f, value);
                }
				else if (item.PropertyInfo.PropertyType == typeof(double))
                {
					var f = item.CSVColumn.Format ?? "{0:f2}";
					s = string.Format(ci, f, value);
                }
                else
                {
                    s = value.ToString();
                }

				result.Add(s);
            }
            return result.JoinString(separator);
        }

		/// <summary>
		/// Gets the header.
		/// </summary>
		/// <returns></returns>
		public static string GetHeader(string separator = "|", string prefix = null, int loop = 0)
		{
			var list = typeof(T).GetProperties(BindingFlags.GetProperty | BindingFlags.Public | BindingFlags.Instance);

			string result = null;
			if (loop > 0 && prefix != null)
			{
				prefix = prefix + loop.ToString();
			}
			foreach (var pi in list)
			{
				object[] attrs = pi.GetCustomAttributes(typeof(ColumnAttribute), true);
				string displayName = string.Empty;
				if (attrs != null && attrs.Length > 0)
				{
					var attribute = attrs[0] as ColumnAttribute;
					if (attribute.Name == null)
					{
						displayName = pi.Name;
					}
					else
					{
						displayName = attribute.Name;
					}
					displayName = prefix + displayName;
					if (pi.PropertyType == typeof(string)
						|| pi.PropertyType == typeof(DateTime))
					{
						displayName = displayName.Replace(separator, " ");
					}
					result = result + displayName + separator;
					continue;
				}

			}

			if (result.EndsWith(separator))
			{
				result = result.Substring(0, result.Length - 1);
			}

			return result;
		}

		#region Helpers

		private static bool IsPartEmpty(string separator, int count, string input)
		{
			string emptyPart = string.Empty;
			for (int i = 0; i < count - 1; i++)
			{
				emptyPart += "\"" + "\"" + separator ;
			}
			emptyPart += "\"" + "\"";
			if (input == emptyPart)
			{
				return true;
			}
			return false;
		}

		private static string GetPartOfInput(Type t, List<string> items, int pos, string separator, out int columnCount)
		{
			columnCount = CountOfCSVColumn(t);
			string partOfInput = null;
			for (int i = pos; i < pos + columnCount; i++)
			{
				partOfInput += "\"" + items[i] + "\"";

				if (i != pos + columnCount - 1)
				{
					partOfInput += separator;
				}
			}
			return partOfInput;
		}

		private static int CountOfCSVColumn(Type t)
		{
			int result = 0;
			// On recherche le nombre de properties
			var piList = t.GetProperties(options);
			foreach (var pi in piList)
			{
				object[] attrs = pi.GetCustomAttributes(typeof(ColumnAttribute), true);
				if (attrs != null && attrs.Length > 0)
				{
					result++;
				}
			}
			return result;
		}

		#endregion
	}
}
